﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl.Types;
using Weazel.Badger.Vhdl.Statements;

#endregion

namespace Weazel.Badger.Vhdl.Expressions
{
  public class ArrayIndexExpression : Expression
  {
    private Expression child;
    public Expression Child
    {
      get
      {
        return child;
      }      
    }

    private Expression to;
    private Expression from;

    private Expression position;
    private bool ranged = false;
		private bool ascending = false;

    public ArrayIndexExpression(
			IComponent parentContainer,
			Expression child, 
			Expression position, 
			GezelType elementGezelType
		) 
			: this(parentContainer, child, position, elementGezelType, (Gezel.Model.Expression) null)
    {
      // empty
    }

    public ArrayIndexExpression(
			IComponent parentContainer,
      Expression child, 
      Expression position, 
      GezelType elementGezelType,       
      Gezel.Model.Expression expr
    ) 
      : base(parentContainer, ((ArrayType)child.Type).ElementType, elementGezelType, expr)
    {
      this.child = child;
      this.position = position;
      this.ranged = false;

      System.Diagnostics.Debug.Assert(child.Type is ArrayType);

      this.Type = ((ArrayType)child.Type).ElementType;
    }

		public ArrayIndexExpression(Expression child, Expression to, Expression from, GezelType elementGezelType)
			: this(child, to, from, false, elementGezelType)
		{
			// empty
		}

    public ArrayIndexExpression(Expression child, Expression to, Expression from, bool ascending, GezelType elementGezelType) 
			: this(null, child, to, from, ascending, elementGezelType, null)
    {
      // empty
    }

		public ArrayIndexExpression(
			IComponent parentContainer,
			Expression child,
			Expression to,
			Expression from,
			GezelType elementGezelType,			
			Gezel.Model.Expression expr
		)
			: this(parentContainer, child, to, from, false, elementGezelType, expr)
		{
		}

    public ArrayIndexExpression(
			IComponent parentContainer,
      Expression child,
      Expression to,
      Expression from,
			bool ascending,
      GezelType elementGezelType,			
      Gezel.Model.Expression expr
    )
      : base(parentContainer, (ArrayType)child.Type, elementGezelType, expr )
    {
      int index1 = GetIndex(to);
      int index2 = GetIndex(from);

      int max = System.Math.Max(index1, index2);
      int min = System.Math.Min(index1, index2);

      this.Type = Vhdl.Types.Types.CreateType(child.Type.TypeId, max - min + 1, false);

      this.child = child;

      this.to = to;
      this.from = from;

      this.ranged = true;
			this.ascending = ascending;
    }

    private int GetIndex(Expression e)
    {
      if (e is ConstantExpression)
      {
        ConstantExpression expr = (ConstantExpression)e;
        return expr.GetValue();
      }
      else if (e is ConstantReferenceExpression)
      {
        ConstantReferenceExpression expr = (ConstantReferenceExpression)e;
        return expr.Constant.GetValue();
      }
      else
      {
        string message =
          "Only simple static integer values supported for VHDL conversion of index expressions";
        throw new VhdlModelException(message);
      }
    }

    // TODO: in case of to==from the type should be element ..


    public override string ToString()
    {
      if (!ranged)
        return String.Format("{0}({1})", child.ToString(), position.ToString());
      else 
        return ascending ? 
					String.Format("{0}({2} to {1})", child.ToString(), from.ToString(), to.ToString())
				: String.Format("{0}({1} downto {2})", child.ToString(), from.ToString(), to.ToString());
    }

    public override bool VerifyType(ref List<VerifyMessage> messages)
    {
      if (this.Type == Types.Types.Undefined)
        return false;

      return true;
    }

    private Dictionary<Types.Type, Types.Type> possibleTypeMap =
      new Dictionary<Weazel.Badger.Vhdl.Types.Type, Weazel.Badger.Vhdl.Types.Type>();

		public override void SetActualType(Entity entity, Process process, Statements.StatementCollection statements, Vhdl.Types.Type type)
    {
      Types.Type integerType = Types.Types.CreateType(Types.Types.Integer, 1, false);

      if (ranged)
      {
        if (type != this.Type)
          throw new VhdlModelException("error resolving array index type");

        TypeConversionResult toConversions = 
          TypeConverter.GetConversion(
            to.GetPossibleTypes(),
            integerType,
            null,
            null,
            false
          );

				to = ConversionBuilder.ApplyConversion(
					toConversions, 
					(Statements.StatementCollection)ParentContainer,
					entity, 
					process,
					to
				);

				to.SetActualType(entity, process, statements, integerType);

        TypeConversionResult fromConversions =
          TypeConverter.GetConversion(
            from.GetPossibleTypes(),
            integerType,
            null,
            null,
            false
          );

				from = ConversionBuilder.ApplyConversion(
					fromConversions, 
					(Statements.StatementCollection)ParentContainer,
					entity, 
					process,
					from
				);

				from.SetActualType(entity, process, statements, integerType);

        return;
      }
      else
      {

        TypeConversionResult conversions =
          TypeConverter.GetConversion(
            position.GetPossibleTypes(),
            integerType,
            null,
            null,
            false
          );

				position = ConversionBuilder.ApplyConversion(
					conversions, 
					(Statements.StatementCollection)ParentContainer,
					entity, 
					process,
					position);

				position.SetActualType(entity, process, statements, integerType);
      }

			child.SetActualType(entity, process, statements, child.Type);

    }

    public override TypeSet GetPossibleTypes()
    {
      // first handle the expression
      if (ranged)
      {
        System.Diagnostics.Debug.Assert(to.GetPossibleTypes().Contains(Types.Types.Integer));
        System.Diagnostics.Debug.Assert(from.GetPossibleTypes().Contains(Types.Types.Integer));

        return new TypeSet(this.Type);
      }
      else
      {
        return new TypeSet(this.Type);


      }


    }
  }
}
